home *** CD-ROM | disk | FTP | other *** search
-
- /* Generated by Interface Builder */
-
- #import <libc.h>
- #import "Monster.h"
- #import "GameBrain.h"
- #import "Player.h"
- #import "Maze.h"
- #import <appkit/NXImage.h>
- #import <appkit/Application.h>
- #import <appkit/graphics.h>
-
- // Get defs of static data arrays which control movement choices
- #import "MonsterMovement.h"
-
-
- @implementation Monster
-
- - init
- {
- return [self initGhost:0 player:nil maze:nil at:0 :0];
- }
-
- - initGhost:(int)num player:(id)pac maze:(id)world at:(int)sx :(int)sy
- { // initialize all instance vars
- [super init];
- ghosts = [NXImage findImageNamed:"Ghosts.tiff"];
- lastx = - 4 * GHOST_SIZE;
- lasty = - 4 * GHOST_SIZE;
- player = pac;
- maze = world;
- eatable = NO;
- state = HOVER;
- myX = sx * GHOST_SIZE; myY = sy * GHOST_SIZE;
- myColor = num;
- px = 2; py = 0;
- loops = 0;
- fix = NO;
-
- return self;
- }
-
- - (BOOL)canBeEaten // returns "eatable".
- { return eatable; }
-
- - move:sender // move one frame
- {
- // use current state to decide which type of movement to do
- switch (state) {
- case HOVER : {
- [self hover];
- break;
- }
- case CHASE : {
- [self chase];
- break;
- }
- case RUN : {
- [self runAway];
- break;
- }
- case HOME : {
- [self goHome];
- break;
- }
- default : { // no movement, since illegal state
- // (we should _never_ get here!)
- px = 0; py = 0;
- break;
- }
- }
- return self;
- }
-
- - hover // hover moves solid ghost when in ghost chamber
- {
- int door_x, door_y;
- int k;
-
- [maze doorPosition:&door_x :&door_y];
-
- if (myX == door_x) {
- if (myY >= (door_y + GHOST_SIZE)) {
- // The ghost is now completely outside the box; we will
- // change its state so that it follows the player around
- // (or runs away if power dot was eaten)
- if (eatable) {
- state = RUN;
- [self runAway];
- } else {
- state = CHASE;
- [self chase];
- }
- return self;
- } else if (myY >= (door_y - 1 - GHOST_SIZE)) {
- // The ghost is directly underneath the door to the outside.
- // Send it out if more than 3 loops made, or 50% chance to leave if
- // at least one loop is complete.
- if ((++loops) > 1) {
- if ((loops > 3) || ((random() & 0x0f) > 7) || eatable) {
- if (eatable) py = 1; // keep right speed
- else py = 2;
- px = 0;
- return self;
- } } } }
- // The rest of the method drives the ghost around the
- // box in a counterclockwise pattern.
- k = 0;
- if (px > 0) {
- if ([maze monsterWall:(myX + 1 + GHOST_SIZE) :myY])
- { k = 1; px = 0; py = 2; }
- } else if (px < 0) {
- if ([maze monsterWall:(myX - 2) :myY])
- { k = 1; px = 0; py = -2; }
- } else if (py > 0) {
- if ([maze monsterWall:myX :(myY + 1 + GHOST_SIZE)])
- { k = 1; px = -2; py = 0; }
- } else if ([maze monsterWall:myX :(myY - 2)])
- { k = 1; px = 2; py = 0; }
- if (eatable && k) {
- px /= 2;
- py /= 2;
- }
- if ((abs(py) == 2) && (myY & 0x1)) fix = YES; // keep synced
- if ((abs(px) == 2) && (myX & 0x1)) fix = YES; // keep synced
- return self;
- }
-
- - runAway // ghost runs away at 1/2 speed after power pill
- {
- int pac_x = [player xpos];
- int pac_y = [player ypos];
- register int tdir = 0x0f, sense;
- int dx = myX % GHOST_SIZE; int dy = myY % GHOST_SIZE;
-
-
- if (dx) tdir &= 0x03; // only allow l/r to sync us up
- if (dy) tdir &= 0x0c; // only allow u/d to sync us up
- // first, find the directions in which this ghost can go
- if ([maze playerWall:(myX+GHOST_SIZE) :myY] || (px < 0)) tdir &= ~0x01;
- if ([maze playerWall:(myX-1) :myY] || (px > 0)) tdir &= ~0x02;
- if ([maze playerWall:myX :(myY-1)] || (py > 0)) tdir &= ~0x04;
- if ([maze playerWall:myX :(myY+GHOST_SIZE)] || (py < 0)) tdir &= ~0x08;
-
- // now choose the new direction for the ghost
- if ((random() & 0x0f) > 4)
- sense = find[sgn(pac_y - myY) + 1][sgn(pac_x - myX) + 1];
- else sense = random() & 0x07;
- px = rxvec[tdir][sense];
- py = ryvec[tdir][sense];
- return self;
- }
-
- - chase // ghost chases player down
- {
- int pac_x = [player xpos];
- int pac_y = [player ypos];
- register int tdir = 0x0f;
- register int sense;
- int dx = myX % GHOST_SIZE; int dy = myY % GHOST_SIZE;
-
- // sync position to block boundaries
- if (px < 0) {
- if (dx > 1) return self;
- if (dx == 1) { px = -1; return self; }
- } else if (px > 0) {
- if ((dx < GHOST_SIZE - 1) && dx) return self;
- if (dx == GHOST_SIZE - 1) { px = 1; return self; }
- } else if (py < 0) {
- if (dy > 1) return self;
- if (dy == 1) { py = -1; return self; }
- } else if (py > 0) {
- if ((dy < GHOST_SIZE - 1) && dy) return self;
- if (dy == GHOST_SIZE - 1) { py = 1; return self; }
- }
-
- // first, find the directions in which this ghost can go
- if ([maze playerWall:(myX+2+GHOST_SIZE) :myY] || (px < 0)) tdir &= ~0x01;
- if ([maze playerWall:(myX-2) :myY] || (px > 0)) tdir &= ~0x02;
- if ([maze playerWall:myX :(myY-2)] || (py > 0)) tdir &= ~0x04;
- if ([maze playerWall:myX :(myY+2+GHOST_SIZE)] || (py < 0)) tdir &= ~0x08;
-
- // now choose the new direction for the ghost
- if ((random() & 0x0f) > 4) // 75% of time, it's algorithmic
- sense = find[sgn(pac_y - myY) + 1][sgn(pac_x - myX) + 1];
- else sense = random() & 0x07;
- px = fxvec[tdir][sense];
- py = fyvec[tdir][sense];
- return self;
- }
-
- - goHome // used to make the eyes run to ghost chamber at 2x speed
- {
- int door_x, door_y;
- register int tdir = 0x0f, sense;
- int dx = myX % GHOST_SIZE; int dy = myY % GHOST_SIZE;
-
- [maze doorPosition:&door_x :&door_y];
- // there, we'll "catch" it and suck it into the box.
- if (myX == door_x) { // using GHOST_SIZE * 2 moves it to bottom of box
- if ((myY >= door_y - GHOST_SIZE * 2) && (myY <= door_y + GHOST_SIZE)) {
- if ((myY >= door_y - GHOST_SIZE) && (myY <= door_y + GHOST_SIZE)) {
- // The ghost is right above the door to the ghost box.
- // We'll send it down into the box. We're assuming
- // here that the ghost box is below the door.
- // If not, the results will be unpredictable.
- px = 0;
- if (dy && (dy < 4)) py = -dy; // sync to blocks
- else py = -4;
- } else if (myY == (door_y - GHOST_SIZE * 2)) {
- // The ghost is all the way inside the box. Here it'll
- // be "reborn" -- its state will be changed to that of a
- // solid ghost hovering inside the ghost box.
- state = HOVER;
- loops = 0;
- eatable = NO;
- px = 2;
- py = 0;
- }
- return self;
- } }
-
- door_y += GHOST_SIZE; // seek the block above the door; when ghost gets
- // now, sync ghost to maze blocks
- if (px < 0) {
- if (dx > 3) return self;
- if (dx) { px = -dx; return self; }
- } else if (px > 0) {
- if ((dx < GHOST_SIZE - 3) && dx) return self;
- else if (dx) { px = GHOST_SIZE - dx; return self; }
- } else if (py < 0) {
- if (dy > 3) return self;
- if (dy) { py = -dy; return self; }
- } else if (py > 0) {
- if ((dy < GHOST_SIZE - 3) && dy) return self;
- else if (dy) { py = GHOST_SIZE - dy; return self; }
- }
-
- if (dx) tdir &= 0x03;
- if (dy) tdir &= 0x0c;
-
- // first, find the directions in which this ghost can go
- if ([maze monsterWall:(myX+4+GHOST_SIZE) :myY] || (px < 0)) tdir &= ~0x01;
- if ([maze monsterWall:(myX-4) :myY] || (px > 0)) tdir &= ~0x02;
- if ([maze monsterWall:myX :(myY-4)] || (py > 0)) tdir &= ~0x04;
- if ([maze monsterWall:myX :(myY+4+GHOST_SIZE)] || (py < 0)) tdir &= ~0x08;
-
- // now choose the new direction for the ghost--not random!
- sense = find[sgn(door_y - myY) + 1][sgn(door_x - myX) + 1];
- px = pxvec[tdir][sense];
- py = pyvec[tdir][sense];
- return self;
- }
-
- - powerDot:(BOOL)eat // called when power dots are eaten or run out.
- {
- if (state != HOME) eatable = eat;
- if ((state == CHASE) && (eat == YES)) {
- state = RUN;
- px = 0; py = 0; // allows quick reverse of direction
- [self runAway];
- }
- if ((state == RUN) && (eat == NO)) {
- state = CHASE;
- if (abs(px) < 2) px *= 2; // ifs should actually be unnecessary...
- if (abs(py) < 2) py *= 2;
- [self chase];
- }
- if (state == HOVER) { // speed up or slow down hovering ghost
- // has to happen here, since hover leaves it alone until it hits
- // an edge of the box...
- if (eat) {
- if (abs(px) == 2) px /=2;
- if (abs(py) == 2) py /= 2;
- } else {
- if (abs(px) == 1) px *=2;
- if (abs(py) == 1) py *= 2;
- } }
- if (eat) {
- powerState = DOT_EATEN;
- powerTime = 0;
- [[NXApp delegate] resetGhostScore];
- } else powerState = DOT_NONE;
- return self;
- }
-
- - (int)munch // called when pac intersects ghost
- {
- if (state == HOME) return HARMLESS;
- if (!eatable) return NO;
- px = 0; py = 0; // allows reverse of direction
- state = HOME;
- [self goHome];
- eatable = NO;
- return YES;
- }
-
- - moveOneFrame
- {
- [super moveOneFrame]; // move the ghost
- if (fix) {
- myY -= sgn(py); // fix for when hovering -- to re-sync
- myX -= sgn(px);
- fix = 0;
- }
- return self;
- }
-
- - renderAt:(int)posx :(int)posy move:(BOOL)moveOk // draw ghost
- {
- int col = myColor & 0x3;
- NXRect from;
- NXPoint pos;
-
- [super renderAt:posx :posy move:moveOk];
-
- // decide to draw invisible or eye ghosts if necessary
- if (eatable && ((powerState != DOT_FADING) || (img & 0x4)))
- col = INVISIBLE_GHOST;
- else if (state == HOME) col = GHOST_EYES;
- NXSetRect(&from,
- ((++img >> 1) & 0x3) * GHOST_SIZE, col * GHOST_SIZE,
- GHOST_SIZE, GHOST_SIZE);
- pos.x = myX + posx; pos.y = myY + posy;
- [ghosts composite:NX_SOVER fromRect:&from toPoint:&pos];
- if (![[NXApp delegate] paused]) {
- if (((++powerTime) >> 4) > flash_ticks[[[NXApp delegate] level]])
- powerState = DOT_FADING;
- if ((powerTime >> 4) > off_ticks[[[NXApp delegate] level]])
- [self powerDot:NO];
- }
- return self;
- }
-
-
- @end
-